5.10. Справочник по Go
Справочник по Go
1. Основные синтаксические элементы
1.1. Комментарии
- Однострочные комментарии начинаются с
//. - Многострочные комментарии заключаются между
/*и*/. - Комментарии, начинающиеся с
//go:, интерпретируются как директивы компилятора (например,//go:noinline).
1.2. Идентификаторы
- Идентификаторы состоят из букв, цифр и символа подчеркивания
_. - Первый символ не может быть цифрой.
- Идентификаторы чувствительны к регистру.
- Идентификаторы, начинающиеся с заглавной буквы, экспортируются из пакета (публичный доступ).
- Идентификаторы, начинающиеся со строчной буквы, имеют внутреннюю видимость (приватный доступ).
1.3. Ключевые слова
Go содержит 25 ключевых слов:
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
Эти слова нельзя использовать в качестве идентификаторов.
1.4. Предопределённые идентификаторы
Следующие идентификаторы предопределены, но не являются ключевыми словами:
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string uint uint8
uint16 uint32 uint64 uintptr true false iota nil
Также предопределены функции:
append,cap,close,complex,copy,delete,imag,len,make,new,panic,print,println,real,recover.
Эти идентификаторы можно переопределять в локальной области видимости, но это не рекомендуется.
2. Типы данных
2.1. Базовые типы
Логический тип
bool: значенияtrueиfalse.
Числовые типы
Целочисленные:
int,int8,int16,int32,int64uint,uint8,uint16,uint32,uint64,uintptr
Псевдонимы:
byte— псевдоним дляuint8rune— псевдоним дляint32(представляет Unicode-кодовую точку)
Вещественные:
float32,float64
Комплексные:
complex64— параfloat32complex128— параfloat64
Строковый тип
string: неизменяемая последовательность байтов в кодировке UTF-8.- Длина строки определяется функцией
len(s), возвращающей количество байтов. - Для получения количества Unicode-символов используется
utf8.RuneCountInString(s).
2.2. Составные типы
Указатели
*T— указатель на значение типаT.- Оператор
&получает адрес переменной. - Оператор
*разыменовывает указатель. - Указатели на нулевые значения равны
nil.
Массивы
[n]T— массив фиксированной длиныnэлементов типаT.- Длина массива является частью его типа.
- Массивы передаются по значению (копируются при присваивании или передаче в функцию).
Срезы (slices)
[]T— срез элементов типаT.- Срез содержит указатель на массив, длину (
len) и вместимость (cap). - Создаются через литералы, функцию
make, или срезание массива/среза. - Пример:
s := []int{1, 2, 3}
Карты (maps)
map[K]V— ассоциативный массив с ключами типаKи значениями типаV.- Ключи должны быть сравнимыми (не содержать срезы, карты, функции).
- Создаются через литералы или
make(map[K]V). - Чтение по отсутствующему ключу возвращает нулевое значение и
falseво втором возвращаемом значении.
Структуры
struct— коллекция полей с именами.- Поля могут быть экспортированы (заглавная буква) или приватными.
- Анонимные поля (встраивание) позволяют наследовать поведение.
Функции
- Тип функции определяется сигнатурой: списком параметров и возвращаемых значений.
- Функции являются значениями первого класса и могут присваиваться переменным, передаваться как аргументы, возвращаться из других функций.
Интерфейсы
interface{}— пустой интерфейс, совместимый с любым типом.- Непустой интерфейс определяет набор методов.
- Реализация интерфейса неявная: если тип реализует все методы интерфейса, он удовлетворяет ему.
Каналы
chan T— канал для передачи значений типаT.- Создаются через
make(chan T). - Поддерживают операции отправки (
ch <- value), получения (<-ch), закрытия (close(ch)). - Могут быть однонаправленными:
chan<- T(только отправка),<-chan T(только получение).
3. Константы и переменные
3.1. Объявление переменных
- Полная форма:
var name Type = value - Краткая форма (внутри функций):
name := value - Групповое объявление:
var (
a int = 1
b string
)
3.2. Константы
- Объявляются с помощью
const. - Значения вычисляются на этапе компиляции.
- Поддерживают
iota— счётчик, начинающийся с 0 внутри блокаconst. - Пример:
const (
Red = iota // 0
Green // 1
Blue // 2
)
3.3. Нулевые значения
Каждый тип имеет нулевое значение:
bool→false- числовые типы →
0 string→""- указатели, срезы, карты, каналы, функции, интерфейсы →
nil
4. Управляющие конструкции
4.1. Условия
if condition { ... }if init; condition { ... }— инициализация перед условием (переменная видна только внутри блокаifиelse)elseиelse ifиспользуются стандартно.
4.2. Циклы
- Единственный цикл в Go —
for. - Формы:
for init; condition; post { ... }for condition { ... }— аналогwhilefor { ... }— бесконечный циклfor key, value := range collection { ... }— итерация по массивам, срезам, строкам, карте, каналам
4.3. Переключатель (switch)
switch expr { case val1: ... default: ... }- Поддерживает выражения в
case. - Автоматический выход из
case(без проваливания, в отличие от C). - Для проваливания используется
fallthrough. - Можно использовать без выражения:
switch { case cond1: ... }— аналог цепочкиif-else.
4.4. Выбор для каналов (select)
select { case ch <- v: ... case x := <-ch2: ... default: ... }- Блокирует выполнение до готовности одного из каналов.
defaultделает выбор неблокирующим.- Если несколько каналов готовы, выбирается случайный.
5. Функции
5.1. Объявление
func name(param1 Type1, param2 Type2) (ret1 RetType1, ret2 RetType2) {
// тело
return value1, value2
}
5.2. Особенности
- Могут возвращать несколько значений.
- Именованные возвращаемые значения инициализируются нулевыми значениями.
- Использование
returnбез аргументов возвращает текущие значения именованных переменных. - Отложенные вызовы (
defer) выполняются после завершения функции, в порядке LIFO.
5.3. Анонимные функции и замыкания
- Функции могут быть определены внутри других функций.
- Замыкания захватывают переменные по ссылке.
5.4. Вариадические функции
- Последний параметр может быть объявлен как
...Type. - Внутри функции доступен как срез
[]Type. - Пример:
func sum(nums ...int) int
6. Методы и интерфейсы
6.1. Методы
- Объявляются с получателем:
func (r ReceiverType) MethodName(...) ... - Получатель может быть значением (
T) или указателем (*T). - Методы с указателем-получателем могут изменять состояние.
6.2. Интерфейсы
- Интерфейс определяет поведение через методы.
- Пустой интерфейс
interface{}принимает любой тип. - Начиная с Go 1.18, поддерживаются параметризованные интерфейсы (в рамках дженериков).
- Проверка реализации:
var _ MyInterface = (*MyType)(nil)
6.3. Type assertion и type switch
value, ok := someInterface.(ConcreteType)okравноtrue, если преобразование возможно.- Type switch:
switch v := x.(type) {
case int:
// v имеет тип int
case string:
// v имеет тип string
}
7. Пакеты и модули
7.1. Пакеты
- Каждый файл начинается с
package name. - Имя исполняемого пакета —
main. - Экспорт осуществляется через заглавные имена.
- Импорт:
import "path/to/package" - Групповой импорт:
import (
"fmt"
"os"
)
7.2. Модули
- Управление зависимостями через
go mod. - Команды:
go mod init <module>— инициализацияgo mod tidy— синхронизация зависимостейgo.mod— файл описания модуляgo.sum— контрольные суммы зависимостей
8. Встроенные функции
Go предоставляет набор предопределённых функций, доступных без импорта:
8.1. make
- Создаёт срезы, карты и каналы.
- Синтаксис:
make([]T, length, capacity)— срезmake(map[K]V, initialCapacity)— картаmake(chan T, bufferSize)— канал
- Возвращает инициализированный экземпляр, а не указатель.
8.2. new
- Выделяет память под значение указанного типа.
- Возвращает указатель на нулевое значение типа:
*T. - Эквивалентно
var t T; return &t.
8.3. len и cap
len(v)возвращает длину:- массива — количество элементов
- среза — количество элементов
- строки — количество байтов
- карты — количество пар ключ-значение
- канала — количество элементов в буфере
cap(v)возвращает вместимость:- среза — максимальное количество элементов без реаллокации
- канала — размер буфера
8.4. append
- Добавляет элементы к срезу:
s = append(s, elem...) - Может выделить новый массив, если текущая вместимость исчерпана.
- Поддерживает распаковку другого среза через
...:append(s1, s2...)
8.5. copy
- Копирует элементы из одного среза в другой:
n := copy(dst, src) - Возвращает количество скопированных элементов (минимум из
len(dst),len(src)). - Работает только со срезами одинакового базового типа.
8.6. delete
- Удаляет пару ключ-значение из карты:
delete(m, key) - Безопасна для вызова с несуществующим ключом.
8.7. close
- Закрывает канал:
close(ch) - После закрытия все последующие операции получения возвращают нулевое значение.
- Отправка в закрытый канал вызывает панику.
- Только отправляющая сторона должна закрывать канал.
8.8. panic и recover
panic(v)немедленно останавливает выполнение функции и начинает раскрутку стека.recover()останавливает раскрутку стека и возвращает значение, переданное вpanic.recoverработает только внутри отложенной (defer) функции.
8.9. Комплексные числа
complex(real, imag)— создаёт комплексное число.real(c)— возвращает вещественную часть.imag(c)— возвращает мнимую часть.
8.10. print и println
- Низкоуровневые функции вывода в stderr.
- Используются только в отладочных целях или при отсутствии стандартной библиотеки.
- Не рекомендуются для production-кода.
9. Обработка ошибок
9.1. Тип error
- Встроенный интерфейс:
type error interface { Error() string } - Ошибки возвращаются как обычные значения, обычно последним аргументом.
- Проверка:
if err != nil {
// обработка
}
9.2. Создание ошибок
errors.New("message")— простая ошибка.fmt.Errorf("format %v", value)— форматированная ошибка.- Начиная с Go 1.13:
fmt.Errorf("...: %w", err)— оборачивает ошибку (wrapping)errors.Is(err, target)— проверяет цепочку ошибок на наличиеtargeterrors.As(err, &target)— пытается преобразовать ошибку к типуtarget
9.3. Пользовательские ошибки
- Реализация интерфейса
error:type MyError struct { Msg string; Code int }
func (e MyError) Error() string { return e.Msg }
9.4. Отказ от игнорирования ошибок
- Идиоматический код всегда проверяет ошибки.
- Игнорирование через
_допустимо только в обоснованных случаях.
10. Конкурентность
10.1. Горутины
- Лёгкие потоки, управляемые планировщиком Go.
- Запускаются с помощью ключевого слова
go:go func() { ... }() - Расход памяти на горутину — от 2 КБ.
- Планировщик использует M:N-модель (M горутин на N системных потоков).
10.2. Каналы
- Основной механизм коммуникации между горутинами.
- Буферизованные каналы:
ch := make(chan int, 10) - Небуферизованные каналы: синхронизируют отправителя и получателя.
- Закрытие канала сигнализирует об окончании передачи данных.
10.3. Select
- Позволяет ждать несколько операций с каналами.
- Поддерживает
defaultдля неблокирующего поведения. - Используется для таймаутов, отмены, объединения потоков.
10.4. sync-примитивы
Пакет sync предоставляет:
MutexиRWMutex— блокировки для защиты общих данных.WaitGroup— ожидание завершения группы горутин.Once— гарантирует однократное выполнение функции.Cond— условная переменная для координации.Pool— пул временных объектов для снижения нагрузки на сборщик мусора.
10.5. Context
Пакет context обеспечивает:
- Отмену операций (
WithCancel) - Таймауты (
WithTimeout) - Дедлайны (
WithDeadline) - Передачу значений (
WithValue) - Широко используется в HTTP-серверах, базах данных, gRPC.
Идиома:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
11. Инструменты разработки
11.1. Компилятор и сборка
go build— компиляция в исполняемый файл.go run— компиляция и запуск в одном шаге.go install— компиляция и установка в$GOBIN.
11.2. Форматирование
gofmt— стандартный инструмент форматирования.go fmt— применяетgofmtко всем файлам пакета.
11.3. Линтинг и анализ
go vet— статический анализ на распространённые ошибки.staticcheck,golangci-lint— сторонние линтеры.
11.4. Тестирование
- Файлы с
_test.go. - Функции:
TestXxx(t *testing.T),BenchmarkXxx(b *testing.B),ExampleXxx(). - Запуск:
go test - Покрытие:
go test -cover
11.5. Профилирование
- Пакет
net/http/pprof— профилирование CPU, памяти, горутин. - Генерация профилей:
go tool pprof binary profile.out
11.6. Документация
godoc(устаревший) илиpkg.go.dev.- Комментарии над объявлением становятся документацией.
- Генерация:
go doc <package>
12. Стандартная библиотека (выборочно)
| Пакет | Назначение |
|---|---|
fmt | Форматированный ввод/вывод |
strconv | Преобразование строк и чисел |
strings, bytes | Работа с текстом и байтами |
io, bufio | Абстракции ввода-вывода |
os, path/filepath | Работа с файловой системой |
net/http | HTTP-клиент и сервер |
encoding/json, xml | Сериализация данных |
time | Работа с датой и временем |
regexp | Регулярные выражения |
crypto/* | Криптографические функции |
database/sql | Унифицированный интерфейс к БД |
flag | Разбор аргументов командной строки |
log | Простое логирование |
13. Дженерики (Generics)
Дженерики были введены в Go 1.18 и позволяют писать переиспользуемый код без привязки к конкретному типу.
13.1. Объявление параметризованных функций
func Print[T any](s []T) {
for _, v := range s {
fmt.Println(v)
}
}
T— параметр типа.any— псевдоним дляinterface{}(ограничение, допускающее любой тип).
13.2. Ограничения типов (type constraints)
- Можно задавать интерфейсы как ограничения:
type Number interface {
int | int32 | int64 | float32 | float64
}
func Sum[T Number](a, b T) T {
return a + b
} - Стандартные ограничения:
comparable(для типов, поддерживающих==и!=).
13.3. Параметризованные типы
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
13.4. Использование в стандартной библиотеке
- Начиная с Go 1.21, в
slices,maps,cmpпоявились дженерик-функции:slices.Contains,slices.SortFuncmaps.Keys,maps.Clonecmp.Compare
13.5. Ограничения и рекомендации
- Дженерики не заменяют интерфейсы.
- Избегайте избыточного использования — применяйте только при реальной необходимости переиспользования логики.
- Дженерики не влияют на производительность: специализация происходит на этапе компиляции.
14. Рефлексия
Пакет reflect позволяет анализировать типы и значения во время выполнения.
14.1. Основные типы
reflect.Type— метаинформация о типе.reflect.Value— значение с возможностью чтения/записи.
14.2. Получение информации
v := reflect.ValueOf(x)
t := v.Type()
14.3. Возможности
- Чтение и запись полей структуры.
- Вызов методов по имени.
- Создание новых значений:
reflect.New(t),reflect.MakeSlice,reflect.MakeMap. - Проверка возможности установки:
v.CanSet().
14.4. Производительность
- Рефлексия медленнее прямого кода.
- Используется в сериализаторах (
json,xml), ORM, тестовых фреймворках. - Кэширование
reflect.Typeулучшает производительность.
14.5. Ограничения
- Нельзя получить исходный идентификатор переменной.
- Нельзя вызвать неэкспортированные методы из другого пакета.
- Работа с указателями требует разыменования.
15. Работа с файловой системой
15.1. Основные операции (пакет os)
os.Create(name)— создаёт файл.os.Open(name)— открывает файл только для чтения.os.OpenFile(name, flag, perm)— гибкое открытие с флагами (O_RDONLY,O_WRONLY,O_CREATE,O_APPENDи т.д.).os.Remove(name)— удаляет файл.os.RemoveAll(path)— рекурсивное удаление директории.
15.2. Чтение и запись
io.ReadFull,io.ReadAll,io.Copy— удобные утилиты.bufio.Scanner— построчное чтение.os.WriteFile,os.ReadFile— чтение/запись всего файла за раз.
15.3. Информация о файле
fi, err := os.Stat(path)— получаетfs.FileInfo.- Поля:
Name(),Size(),Mode(),ModTime(),IsDir().
15.4. Путь и навигация
filepath.Join,filepath.Dir,filepath.Base,filepath.Extfilepath.Walk— рекурсивный обход дерева каталогов.
15.5. Временные файлы
os.CreateTemp(dir, pattern)— безопасное создание временного файла.ioutil.TempDir(устарело, заменено наos.MkdirTemp).
16. Сетевое программирование
16.1. Низкоуровневые сокеты (net)
net.Dial("tcp", "host:port")— клиентское соединение.net.Listen("tcp", ":8080")— серверное прослушивание.- Поддержка TCP, UDP, Unix-сокетов.
16.2. HTTP-сервер
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
http.ListenAndServe(":8080", nil)
16.3. HTTP-клиент
http.Get,http.Post— простые запросы.http.Client— настраиваемый клиент с таймаутами, куками, прокси.- Поддержка HTTPS, редиректов, keep-alive.
16.4. Middleware и маршрутизация
- Стандартный
http.ServeMuxподдерживает базовую маршрутизацию. - Популярные сторонние маршрутизаторы:
gorilla/mux,chi,gin,echo.
16.5. WebSocket, gRPC, GraphQL
golang.org/x/net/websocket— официальный (упрощённый) WebSocket.google.golang.org/grpc— реализация gRPC.- Сторонние библиотеки для GraphQL:
graphql-go,gqlgen.
17. Сборка и развёртывание
17.1. Кросскомпиляция
- Установка переменных окружения:
GOOS=linux GOARCH=amd64 go build -o app-linux main.go - Поддерживаемые ОС и архитектуры:
go tool dist list
17.2. Флаги компилятора
-ldflags="-s -w"— убирает отладочную информацию (уменьшает размер).-trimpath— удаляет пути к исходникам из бинарника.-buildvcs=false— отключает внедрение информации о VCS.
17.3. Статическая линковка
- По умолчанию бинарники статически скомпонованы (кроме CGO).
- Для CGO:
CGO_ENABLED=0— отключает CGO, обеспечивает полную статическую сборку.
17.4. Docker-образы
- Минимальный образ:
FROM scratchилиFROM alpine. - Многоступенчатая сборка:
FROM golang:1.23 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
FROM scratch
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["/myapp"]
17.5. Версионирование
- Использование
go.modс семантическим версионированием. - Теги в Git:
v1.2.3— автоматически распознаются как версии модуля.
18. Производительность и оптимизация
18.1. Аллокации
- Минимизация выделения памяти в горячем коде.
- Использование пулов (
sync.Pool) для временных объектов. - Предварительное выделение срезов:
make([]T, 0, capacity)
18.2. Профилирование
- CPU profile:
pprof.StartCPUProfile - Heap profile:
pprof.WriteHeapProfile - Goroutine profile:
/debug/pprof/goroutine
18.3. Escape analysis
- Компилятор определяет, где хранить переменную — в стеке или куче.
- Флаг
-gcflags="-m"показывает результат анализа.
18.4. Benchmarking
- Написание бенчмарков:
func BenchmarkAppend(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = append([]int{}, 1)
}
} - Запуск:
go test -bench=.
18.5. Конкурентность vs параллелизм
- Горутины не гарантируют параллельное выполнение.
- Установка
GOMAXPROCS(n)управляет числом системных потоков. - По умолчанию
GOMAXPROCSравно числу логических CPU.
19. Архитектурные рекомендации
19.1. Структура проекта
Рекомендуемая структура (в соответствии с Standard Go Project Layout):
/cmd/
/myapp/
main.go
/internal/
/handlers/
/services/
/repositories/
/pkg/
/utils/
/validators/
/api/
/proto/
/openapi/
/config/
/docs/
/scripts/
/test/
/go.mod
19.2. Разделение ответственности
main— только инициализация.- Бизнес-логика — в
/internal. - Переиспользуемые компоненты — в
/pkg. - Конфигурация — через флаги, переменные окружения или конфиг-файлы.
19.3. Обработка ошибок
- Централизованное логирование ошибок.
- Использование контекста для отмены.
- Оборачивание ошибок с контекстом:
fmt.Errorf("failed to connect: %w", err)
19.4. Тестирование
- Таблицы тестов (
test cases). - Моки через интерфейсы.
- Интеграционные тесты с
testcontainersилиdockertest.
19.5. Логирование
- Использование структурированного логгера:
zap,log/slog(Go 1.21+). - Избегание
fmt.Printlnв production.
20. Переменные окружения и конфигурация
20.1. Чтение
os.Getenv("KEY")— получение значения.os.LookupEnv("KEY")— проверка существования.
20.2. Библиотеки
github.com/spf13/viper— поддержка JSON, YAML, TOML, env, флагов.github.com/kelseyhightower/envconfig— привязка env-переменных к структуре.
20.3. Безопасность
- Не логировать чувствительные переменные (пароли, токены).
- Использовать
.envфайлы только в разработке.
21. Работа с базами данных
21.1. Пакет database/sql
- Стандартный интерфейс для реляционных баз данных.
- Требует драйвера (например,
github.com/lib/pqдля PostgreSQL,github.com/go-sql-driver/mysqlдля MySQL).
21.2. Подключение
db, err := sql.Open("postgres", "user=... password=... dbname=...")
if err != nil { /* handle */ }
defer db.Close()
sql.Openне устанавливает соединение немедленно — проверка черезdb.Ping().
21.3. Выполнение запросов
db.Query— выборка (SELECT)db.Exec— модификация (INSERT,UPDATE,DELETE)db.Prepare— подготовленные выражения (повторное использование)
21.4. Обработка результатов
rows, _ := db.Query("SELECT id, name FROM users")
defer rows.Close()
for rows.Next() {
var id int
var name string
rows.Scan(&id, &name)
}
21.5. Транзакции
tx, _ := db.Begin()
_, _ = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
_, _ = tx.Exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
tx.Commit() // или tx.Rollback()
21.6. ORM и query builders
- GORM — полнофункциональный ORM с поддержкой ассоциаций, предзагрузки, миграций.
- SQLBoiler, Ent, Squirrel — альтернативы с разным уровнем абстракции.
- Рекомендация: начинать с
database/sql, переходить к ORM при сложной доменной модели.
22. Миграции баз данных
22.1. Инструменты
- Goose: поддержка SQL и Go-миграций, откаты.
- Flyway (через CLI), Liquibase — внешние инструменты.
- GORM AutoMigrate — автоматическая синхронизация схемы (не для production).
22.2. Структура миграции (Goose)
-- +goose Up
CREATE TABLE users (...);
-- +goose Down
DROP TABLE users;
22.3. Применение
goose -dir migrations postgres "..." up
22.4. Best practices
- Каждая миграция — атомарна.
- Никогда не изменять применённую миграцию.
- Использовать транзакции там, где это поддерживается СУБД.
23. Валидация входных данных
23.1. Встроенные средства
- Проверка через условия:
if len(name) == 0 { ... } - Использование
errors.Newилиfmt.Errorf.
23.2. Библиотеки
- go-playground/validator — теги структур:
type User struct {
Email string `validate:"required,email"`
Age int `validate:"gte=0,lte=130"`
} - Поддержка кастомных валидаторов, локализации ошибок.
23.3. HTTP-валидация
- Валидация тела запроса после декодирования JSON.
- Возврат ошибок в формате JSON с кодом
400 Bad Request.
24. CLI-приложения
24.1. Стандартные средства
flag— простой парсер флагов.os.Args— прямой доступ к аргументам.
24.2. Расширенные библиотеки
- urfave/cli — подкоманды, флаги, help-система.
- cobra — мощный фреймворк (используется в Kubernetes, Helm).
- Поддержка автодополнения, конфигурационных файлов, версионирования.
24.3. Пример (cobra)
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "A brief description",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello")
},
}
24.4. Best practices
- Чёткая документация через
--help. - Использование
exit code0 при успехе, ненулевой — при ошибках. - Логирование в
stderr, данные — вstdout.
25. Обработка сигналов операционной системы
25.1. Пакет os/signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan // ожидание сигнала
25.2. Graceful shutdown
- Остановка HTTP-сервера:
srv := &http.Server{Addr: ":8080", Handler: handler}
go func() {
<-sigChan
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
srv.Shutdown(ctx)
}()
srv.ListenAndServe()
25.3. Поддерживаемые сигналы
os.Interrupt— Ctrl+Csyscall.SIGTERM— завершение от менеджера процессов (systemd, Docker)syscall.SIGHUP— перезагрузка конфигурации (опционально)
26. Работа с временными зонами
26.1. Тип time.Time
- Хранит время в UTC, но содержит информацию о временной зоне.
time.Now()— текущее локальное время.time.Now().UTC()— текущее время в UTC.
26.2. Загрузка временных зон
time.LoadLocation("Europe/Moscow")- Использование:
t.In(loc)
26.3. Форматирование
- Константы:
time.RFC3339,time.ANSIC - Кастомный формат:
"2006-01-02 15:04:05 MST"
Примечание: Go использует фиксированную дату
Mon Jan 2 15:04:05 MST 2006как шаблон.
26.4. Парсинг
time.Parse(layout, value)— парсинг строки.time.ParseInLocation(layout, value, loc)— с учётом временной зоны.
27. Юнит-тестирование с моками
27.1. Подход через интерфейсы
- Зависимости объявляются как интерфейсы.
- Реализация — в production, мок — в тестах.
27.2. Генерация моков
- GoMock (
gomock): генерация моков из интерфейсов. - Testify/mock: ручное или полуавтоматическое создание.
27.3. Пример (Testify)
type MockDB struct {
mock.Mock
}
func (m *MockDB) GetUser(id int) (User, error) {
args := m.Called(id)
return args.Get(0).(User), args.Error(1)
}
// В тесте:
db := new(MockDB)
db.On("GetUser", 1).Return(User{Name: "Alice"}, nil)
27.4. Assertion
testify/assert:assert.Equal(t, expected, actual)testify/require: останавливает тест при провале.
28. CI/CD для проектов на Go
28.1. GitHub Actions
Пример workflow:
name: Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: '1.23' }
- run: go test -v ./...
28.2. Проверки
go vetgofmt -s -l .(проверка форматирования)go mod tidy && git diff --exit-code go.mod go.sumstaticcheck ./...
28.3. Публикация
- Сборка бинарников для разных ОС/архитектур.
- Прикрепление артефактов к релизам.
- Публикация модуля через Git-теги (
v1.2.3).
29. Лицензирование
29.1. Выбор лицензии
- MIT — минималистичная, разрешающая.
- Apache 2.0 — с явным указанием авторства и патентной защитой.
- GPL — требует открытости производных работ (редко используется в Go-экосистеме).
29.2. Указание в проекте
- Файл
LICENSEв корне. - Комментарий в
go.mod:// license MIT
29.3. Проверка зависимостей
go list -m -json all | jq '.[].Indirect'— анализ косвенных зависимостей.- Инструменты:
license-checker,scancode.
30. Паттерны проектирования в Go
30.1. Options Pattern
Гибкая настройка структур:
type Server struct { Port int; TLS bool }
type Option func(*Server)
func WithPort(p int) Option { return func(s *Server) { s.Port = p } }
func NewServer(opts ...Option) *Server {
s := &Server{Port: 8080}
for _, opt := range opts {
opt(s)
}
return s
}
30.2. Builder Pattern
Построение сложных объектов шаг за шагом.
30.3. Middleware Pattern
Цепочка обработчиков:
type Middleware func(http.Handler) http.Handler
30.4. Repository Pattern
Абстрагирование доступа к данным:
- Интерфейс
UserRepository - Реализация
PostgresUserRepository
30.5. Dependency Injection
- Ручная передача зависимостей.
- Использование контейнеров: dig, wire (Google).
31. Антипаттерны и распространённые ошибки
- Возврат указателя на локальную переменную в цикле.
- Игнорирование ошибок (
_ = f()). - Захват переменной цикла в замыкании без копирования.
- Передача мьютекса по значению.
- Использование глобального состояния без синхронизации.
- Избыточное использование рефлексии и дженериков.
- Отсутствие контекста в длительных операциях.